package com.njtoyo.taxi.admin.service.open.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.ds.DSFactory;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.StyleSet;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njtoyo.taxi.admin.cache.PrimaryCachePutter;
import com.njtoyo.taxi.admin.library.Const;
import com.njtoyo.taxi.admin.mapper.master.entity.*;
import com.njtoyo.taxi.admin.mapper.master.enums.*;
import com.njtoyo.taxi.admin.mapper.master.enums.SimpleCallRideState;
import com.njtoyo.taxi.admin.mapper.master.mapper.*;
import com.njtoyo.taxi.admin.mapper.master.mapper.data.OpenFunnelMapper;
import com.njtoyo.taxi.admin.mapper.secondary.entity.data.CallTaxiKpi;
import com.njtoyo.taxi.admin.mapper.secondary.mapper.data.CallTaxiKpiMapper;
import com.njtoyo.taxi.admin.mongo.entity.primary.*;
import com.njtoyo.taxi.admin.mongo.repository.primary.DeviceListenCallMessageLogRepository;
import com.njtoyo.taxi.admin.mongo.repository.primary.DriverGrabCallInfoMongoRepository;
import com.njtoyo.taxi.admin.mongo.repository.primary.RealtimeCallExcludeTaxiRepository;
import com.njtoyo.taxi.admin.mongo.repository.primary.RealtimeCallMqttMsgMongoRepository;
import com.njtoyo.taxi.admin.rest.presenter.data.funnel.CancelDataVO;
import com.njtoyo.taxi.admin.rest.wrapper.open.funnel.FunnelRealTimeCallListWrapper;
import com.njtoyo.taxi.admin.service.business.ride.RideService;
import com.njtoyo.taxi.admin.service.open.OpenFunnelService;
import com.njtoyo.taxi.admin.utils.RAMPageUtil;
import com.taxi.entity.common.RestResult;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.CellStyle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@Slf4j
@DS(Const.PRIMARY_DS)
public class OpenFunnelServiceImpl implements OpenFunnelService {


    @Autowired
    private RideMapper rideMapper;
    @Autowired
    private SimpleCallRideMapper simpleCallRideMapper;
    @Autowired
    private SimpleCallMapper simpleCallMapper;
    @Autowired
    private DriverConfirmCallPersistedMapper driverConfirmCallPersistedMapper;
    @Autowired
    private RideOrderMapper rideOrderMapper;
    @Autowired
    private SimpleCallRideOrderMapper simpleCallRideOrderMapper;
    @Autowired
    private RideService rideService;
    @Autowired
    private TaxiMapper taxiMapper;
    @Autowired
    private RealtimeCallMapper realtimeCallMapper;
    @Autowired
    private DriverGrabRealtimePersistedMapper driverGrabRealtimePersistedMapper;
    @Autowired
    private DriverGrabSimpleCallPersistedMapper driverGrabSimpleCallPersistedMapper;
    @Autowired
    private DriverMapper driverMapper;
    @Autowired
    private OpenFunnelMapper openFunnelMapper;
    @Autowired
    private RideCancellationMapper rideCancellationMapper;
    @Autowired
    private SimpleCallRideCancellationMapper simpleCallRideCancellationMapper;
    @Autowired
    private SimpleCallCustomerMapper simpleCallCustomerMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private DriverTagMapper driverTagMapper;
    @Autowired
    private DeviceListenCallMessageLogRepository deviceListenCallMessageLogRepository;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private PrimaryCachePutter primaryCachePutter;
    @Autowired
    private CallTaxiKpiMapper callTaxiKpiMapper;
    @Autowired
    private CallScreenLogMapper callScreenLogMapper;
    @Autowired
    private CallPrepayOrderMapper callPrepayOrderMapper;
    @Autowired
    private RealtimeCallMqttMsgMongoRepository realtimeCallMqttMsgMongoRepository;
    @Autowired
    private ReportEventLogMapper reportEventLogMapper;
    @Autowired
    private RealtimeCallExcludeTaxiRepository realtimeCallExcludeTaxiRepository;
    @Autowired
    private DriverGrabCallInfoMongoRepository driverGrabCallInfoMongoRepository;

    @Override
    public RestResult getCountToDayRide(Map<String, Object> params) {

        Integer cantonId = MapUtil.getInt(params, "cantonId");
        String type = MapUtil.getStr(params, "type");

        int sendNum = 0;
        int joinCount = 0;
        int outCarNum = 0;
        int callNum = 0;
        int completeNum = 0;
        int repeatNum = 0;
        int prepayNum = 0;
        int prepayNotNum = 0;
        int driverTalkNum = 0;//报单数
        int pickNum = 0;//接单数
        int effectiveCallNum = 0;
        int completeDriverNum = 0;
        //听单中
        int waitOrderDriver = 0;
        //运营中
        int operationDriver = 0;
        if ("real_time".equals(type)) {
            Object valueObj = primaryCachePutter.getRedisTemplate().opsForValue().get("mass:" + cantonId + ":" + DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_PATTERN));
            if (ObjectUtil.isNotNull(valueObj)) {
                JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(valueObj));
                callNum = jsonObject.getIntValue("call");
                joinCount = jsonObject.getIntValue("join");
                outCarNum = jsonObject.getIntValue("taxi");
                completeNum = jsonObject.getIntValue("ride");
                completeDriverNum = jsonObject.getIntValue("ride_driver");
                prepayNum = jsonObject.getIntValue("prepay");
                prepayNotNum = jsonObject.getIntValue("unpaid_prepay");

                pickNum = jsonObject.getIntValue("pick");

                sendNum = jsonObject.getIntValue("notice");
                repeatNum = jsonObject.getIntValue("redundant");
                driverTalkNum = jsonObject.getIntValue("report");

                effectiveCallNum = callNum - repeatNum < 0 ? 0 : callNum - repeatNum;

                waitOrderDriver = openFunnelMapper.waitOrderDriver(cantonId);
                operationDriver = openFunnelMapper.operationDriver(cantonId);
            }

        } else if ("phone".equals(type)) {
            List<SimpleCall> calls = simpleCallMapper.selectList(Wrappers.<SimpleCall>lambdaQuery().eq(SimpleCall::getCantonId, cantonId).in(SimpleCall::getCategory, Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment)).between(SimpleCall::getCreatedAt, DateUtil.beginOfDay(DateUtil.date()), DateUtil.endOfDay(DateUtil.date())));
            if (CollUtil.isNotEmpty(calls)) {
                List<Long> callIds = calls.stream().map(simpleCall -> simpleCall.getId()).collect(Collectors.toList());
                completeNum = simpleCallRideMapper.selectCount(Wrappers.<SimpleCallRide>lambdaQuery().in(SimpleCallRide::getRefId, callIds).eq(SimpleCallRide::getState, SimpleCallRideState.end));
                sendNum = driverConfirmCallPersistedMapper.selectCountSendNum(callIds, 2);
                joinCount = driverGrabSimpleCallPersistedMapper.selectCountJoinDriver(callIds);
                repeatNum = openFunnelMapper.getSimpleCallRepeatNum(callIds);
                callNum = callIds.size();
            }
        } else if ("help_old".equals(type)) {
            List<SimpleCall> calls = simpleCallMapper.selectList(Wrappers.<SimpleCall>lambdaQuery().eq(SimpleCall::getCantonId, cantonId).in(SimpleCall::getCategory, Arrays.asList(SimpleCallRideCategory.old_simple)).between(SimpleCall::getCreatedAt, DateUtil.beginOfDay(DateUtil.date()), DateUtil.endOfDay(DateUtil.date())));
            if (CollUtil.isNotEmpty(calls)) {
                List<Long> callIds = calls.stream().map(simpleCall -> simpleCall.getId()).collect(Collectors.toList());
                completeNum = simpleCallRideMapper.selectCount(Wrappers.<SimpleCallRide>lambdaQuery().in(SimpleCallRide::getRefId, callIds).eq(SimpleCallRide::getState, SimpleCallRideState.end));
                sendNum = driverConfirmCallPersistedMapper.selectCountSendNum(callIds, 2);
                joinCount = driverGrabSimpleCallPersistedMapper.selectCountJoinDriver(callIds);
                repeatNum = openFunnelMapper.getSimpleCallRepeatNum(callIds);
                callNum = callIds.size();
            }
        }

        List<Driver> drivers = driverMapper.selectList(Wrappers.<Driver>lambdaQuery().eq(Driver::getCantonId, cantonId));
        if (CollUtil.isNotEmpty(drivers)) {
            List<Long> taxiIds = drivers.stream().map(Driver::getTaxiId).collect(Collectors.toList());
            outCarNum = taxiMapper.selectCount(Wrappers.<Taxi>lambdaQuery().in(Taxi::getIsAvailable, 1).in(Taxi::getId, taxiIds));
        }

        Map<String, Integer> result = new HashMap<>();
        result.put("callNum", callNum);
        result.put("completeNum", completeNum);
        result.put("sendNum", sendNum);
        result.put("joinCount", joinCount);
        result.put("repeatNum", repeatNum);
        result.put("prepayNum", prepayNum);
        result.put("prepayNotNum", prepayNotNum);
        result.put("driverTalkNum", driverTalkNum);
        result.put("completeDriverNum", completeDriverNum);
        result.put("outCarNum", ObjectUtil.isNull(outCarNum) ? 0 : outCarNum);
        result.put("effectiveCallNum", effectiveCallNum);
        result.put("pickNum", pickNum);
        result.put("waitOrderDriverNum", waitOrderDriver);
        result.put("operationDriverNum", operationDriver);
        return RestResult.success(result);
    }

    @Override
    public RestResult getFinishOrderByHour(Map<String, Object> params) {
        Integer cantonId = MapUtil.getInt(params, "cantonId");
        String type = MapUtil.getStr(params, "type");
        Date todayStart = DateUtil.beginOfDay(DateUtil.date());
        Date todayEnd = DateUtil.endOfDay(DateUtil.date());
        List<Map<String, Object>> todayOrders = null;
        List<Map<String, Object>> yesterdayOrders = null;
        List<Map<String, Object>> weekAgoOrders = null;
        if ("real_time".equals(type)) {
            todayOrders = callTaxiKpiMapper.selectRideByHour(DateUtil.format(todayStart, DatePattern.PURE_DATE_PATTERN), cantonId);
            yesterdayOrders = callTaxiKpiMapper.selectRideByHour(DateUtil.format(DateUtil.offsetDay(todayStart, -1), DatePattern.PURE_DATE_PATTERN), cantonId);
            weekAgoOrders = callTaxiKpiMapper.selectRideByHour(DateUtil.format(DateUtil.offsetDay(todayStart, -7), DatePattern.PURE_DATE_PATTERN), cantonId);
        } else if ("phone".equals(type)) {
            todayOrders = simpleCallRideOrderMapper.selectByHour(Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment), todayStart, todayEnd, cantonId);
            yesterdayOrders = simpleCallRideOrderMapper.selectByHour(Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment), DateUtil.offsetDay(todayStart, -1), DateUtil.offsetDay(todayEnd, -1), cantonId);
            weekAgoOrders = simpleCallRideOrderMapper.selectByHour(Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment), DateUtil.offsetDay(todayStart, -7), DateUtil.offsetDay(todayEnd, -7), cantonId);
        } else if ("help_old".equals(type)) {
            todayOrders = simpleCallRideOrderMapper.selectByHour(Arrays.asList(SimpleCallRideCategory.old_simple), todayStart, todayEnd, cantonId);
            yesterdayOrders = simpleCallRideOrderMapper.selectByHour(Arrays.asList(SimpleCallRideCategory.old_simple), DateUtil.offsetDay(todayStart, -1), DateUtil.offsetDay(todayEnd, -1), cantonId);
            weekAgoOrders = simpleCallRideOrderMapper.selectByHour(Arrays.asList(SimpleCallRideCategory.old_simple), DateUtil.offsetDay(todayStart, -7), DateUtil.offsetDay(todayEnd, -7), cantonId);
        }
        Map<String, Object> result = new HashMap<>();
        result.put("todayOrders", todayOrders);
        result.put("yesterdayOrders", yesterdayOrders);
        result.put("weekAgoOrders", weekAgoOrders);
        return RestResult.success(result);
    }

    @Override
    public RestResult getFinishOrderByDay(Map<String, Object> params) {
        Integer cantonId = MapUtil.getInt(params, "cantonId");
        String type = MapUtil.getStr(params, "type");


        Date todayEnd = DateUtil.date();
        Date todayStart = DateUtil.beginOfMonth(todayEnd);

        List<Map<String, Object>> dayOrders = null;
        if ("real_time".equals(type)) {
            dayOrders = callTaxiKpiMapper.selectFinishRideByDayOfMonth(cantonId, DateUtil.format(todayStart, DatePattern.PURE_DATE_PATTERN), DateUtil.format(todayEnd, DatePattern.PURE_DATE_PATTERN));
        } else if ("phone".equals(type)) {
            dayOrders = simpleCallRideOrderMapper.selectFinishByDay(Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment), todayStart, todayEnd, cantonId);
        } else if ("help_old".equals(type)) {
            dayOrders = simpleCallRideOrderMapper.selectFinishByDay(Arrays.asList(SimpleCallRideCategory.old_simple), todayStart, todayEnd, cantonId);
        }
        Map<String, Object> result = new HashMap<>();
        result.put("dayOrders", dayOrders);
        return RestResult.success(result);
    }

    @Override
    public RestResult get30DayCalls(Map<String, Object> params) {
        Integer cantonId = MapUtil.getInt(params, "cantonId");
        String type = MapUtil.getStr(params, "type");
        List<Map<String, Object>> result = null;

        Date endDate = DateUtil.date();
        Date startDate = DateUtil.offsetDay(endDate, -30);

        if ("real_time".equals(type)) {

            result = callTaxiKpiMapper.get30DayOfCallAndFinish(cantonId, DateUtil.format(startDate, DatePattern.PURE_DATE_PATTERN), DateUtil.format(endDate, DatePattern.PURE_DATE_PATTERN));
        } else if ("phone".equals(type)) {
            result = openFunnelMapper.get30DaySimpleCalls(Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment), startDate, endDate, cantonId);
        } else if ("help_old".equals(type)) {
            result = openFunnelMapper.get30DaySimpleCalls(Arrays.asList(SimpleCallRideCategory.old_simple), startDate, endDate, cantonId);
        }
        return RestResult.success(result);
    }

    @Override
    public RestResult get30DayGrapNum(Map<String, Object> params) {
        Integer cantonId = MapUtil.getInt(params, "cantonId");
        String type = MapUtil.getStr(params, "type");

        Date endDate = DateUtil.date();
        Date startDate = DateUtil.offsetDay(endDate, -30);
        List<Map<String, Object>> result = null;
        if ("real_time".equals(type)) {
            result = callTaxiKpiMapper.selectDrverRealTimeSendAndGrapByDay(cantonId, DateUtil.format(startDate, DatePattern.PURE_DATE_PATTERN), DateUtil.format(endDate, DatePattern.PURE_DATE_PATTERN));
        } else if ("phone".equals(type)) {
            result = openFunnelMapper.selectDrverSimpleSendAndGrapByDay(Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment), startDate, endDate, cantonId);
        } else if ("help_old".equals(type)) {
            result = openFunnelMapper.selectDrverSimpleSendAndGrapByDay(Arrays.asList(SimpleCallRideCategory.old_simple), startDate, endDate, cantonId);
        }
        return RestResult.success(result);
    }

    @Override
    public RestResult sendByHour(Map<String, Object> params) {
        Integer cantonId = MapUtil.getInt(params, "cantonId");
        String type = MapUtil.getStr(params, "type");
        Date todayStart = DateUtil.beginOfDay(DateUtil.date());
        Date todayEnd = DateUtil.endOfDay(DateUtil.date());
        List<Map<String, Object>> todayConfirms = null;
        List<Map<String, Object>> yesterdayConfirms = null;
        List<Map<String, Object>> weekAgoConfirms = null;
        if ("real_time".equals(type)) {

            todayConfirms = callTaxiKpiMapper.selectSendDriverMQTTByHour(DateUtil.format(todayStart, DatePattern.PURE_DATE_PATTERN), cantonId);
            yesterdayConfirms = callTaxiKpiMapper.selectSendDriverMQTTByHour(DateUtil.format(DateUtil.offsetDay(todayStart, -1), DatePattern.PURE_DATE_PATTERN), cantonId);
            weekAgoConfirms = callTaxiKpiMapper.selectSendDriverMQTTByHour(DateUtil.format(DateUtil.offsetDay(todayStart, -7), DatePattern.PURE_DATE_PATTERN), cantonId);

        } else if ("phone".equals(type)) {
            todayConfirms = openFunnelMapper.selectDrverSimpleCallSendByHour(Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment), todayStart, todayEnd, cantonId);
            yesterdayConfirms = openFunnelMapper.selectDrverSimpleCallSendByHour(Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment), DateUtil.offsetDay(todayStart, -1), DateUtil.offsetDay(todayEnd, -1), cantonId);
            weekAgoConfirms = openFunnelMapper.selectDrverSimpleCallSendByHour(Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment), DateUtil.offsetDay(todayStart, -7), DateUtil.offsetDay(todayEnd, -7), cantonId);
        } else if ("help_old".equals(type)) {
            todayConfirms = openFunnelMapper.selectDrverSimpleCallSendByHour(Arrays.asList(SimpleCallRideCategory.old_simple), todayStart, todayEnd, cantonId);
            yesterdayConfirms = openFunnelMapper.selectDrverSimpleCallSendByHour(Arrays.asList(SimpleCallRideCategory.old_simple), DateUtil.offsetDay(todayStart, -1), DateUtil.offsetDay(todayEnd, -1), cantonId);
            weekAgoConfirms = openFunnelMapper.selectDrverSimpleCallSendByHour(Arrays.asList(SimpleCallRideCategory.old_simple), DateUtil.offsetDay(todayStart, -7), DateUtil.offsetDay(todayEnd, -7), cantonId);
        }
        Map<String, Object> result = new HashMap<>();
        result.put("todayConfirms", todayConfirms);
        result.put("yesterdayConfirms", yesterdayConfirms);
        result.put("weekAgoConfirms", weekAgoConfirms);
        return RestResult.success(result);
    }

    @Override
    public RestResult grabByHour(Map<String, Object> params) {
        Integer cantonId = MapUtil.getInt(params, "cantonId");
        String type = MapUtil.getStr(params, "type");
        Date todayStart = DateUtil.beginOfDay(DateUtil.date());
        Date todayEnd = DateUtil.endOfDay(DateUtil.date());
        List<Map<String, Object>> todayGrabs = null;
        List<Map<String, Object>> yesterdayGrabs = null;
        List<Map<String, Object>> weekAgoGrabs = null;
        if ("real_time".equals(type)) {

            todayGrabs = callTaxiKpiMapper.selectDriverGrabByHour(DateUtil.format(todayStart, DatePattern.PURE_DATE_PATTERN), cantonId);
            yesterdayGrabs = callTaxiKpiMapper.selectDriverGrabByHour(DateUtil.format(DateUtil.offsetDay(todayStart, -1), DatePattern.PURE_DATE_PATTERN), cantonId);
            weekAgoGrabs = callTaxiKpiMapper.selectDriverGrabByHour(DateUtil.format(DateUtil.offsetDay(todayStart, -7), DatePattern.PURE_DATE_PATTERN), cantonId);

        } else if ("phone".equals(type)) {
            todayGrabs = openFunnelMapper.selectDriverSimpleCallGrabByHour(Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment), todayStart, todayEnd, cantonId);
            yesterdayGrabs = openFunnelMapper.selectDriverSimpleCallGrabByHour(Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment), DateUtil.offsetDay(todayStart, -1), DateUtil.offsetDay(todayEnd, -1), cantonId);
            weekAgoGrabs = openFunnelMapper.selectDriverSimpleCallGrabByHour(Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment), DateUtil.offsetDay(todayStart, -7), DateUtil.offsetDay(todayEnd, -7), cantonId);
        } else if ("help_old".equals(type)) {
            todayGrabs = openFunnelMapper.selectDriverSimpleCallGrabByHour(Arrays.asList(SimpleCallRideCategory.old_simple), todayStart, todayEnd, cantonId);
            yesterdayGrabs = openFunnelMapper.selectDriverSimpleCallGrabByHour(Arrays.asList(SimpleCallRideCategory.old_simple), DateUtil.offsetDay(todayStart, -1), DateUtil.offsetDay(todayEnd, -1), cantonId);
            weekAgoGrabs = openFunnelMapper.selectDriverSimpleCallGrabByHour(Arrays.asList(SimpleCallRideCategory.old_simple), DateUtil.offsetDay(todayStart, -7), DateUtil.offsetDay(todayEnd, -7), cantonId);
        }
        Map<String, Object> result = new HashMap<>();
        result.put("todayGrabs", todayGrabs);
        result.put("yesterdayGrabs", yesterdayGrabs);
        result.put("weekAgoGrabs", weekAgoGrabs);
        return RestResult.success(result);
    }

    @Override
    public RestResult getTodayRide(Integer current, Integer size, Integer cantonId, String type, String date) {
        if (StrUtil.isBlank(date)) {
            date = DateUtil.now();
        }
        //筛选日期格式化
        DateTime t = DateUtil.parse(date, DatePattern.NORM_DATE_PATTERN);//yyyy-MM-dd
        DateTime stime = DateUtil.beginOfDay(t);
        DateTime etime = DateUtil.endOfDay(t);
        IPage<Map<String, Object>> res = new Page<>();
        List<Map<String, Object>> list = new ArrayList<>();
        Page page = new Page(current, size);
        Page<Object> pages = null;
        if ("real_time".equals(type)) {
            IPage<FunnelRealTimeCallListWrapper> realTimeCalls = openFunnelMapper.getRealTimeCalls(page, cantonId, stime, etime);
            pages = new Page<>();
            pages.setCurrent(realTimeCalls.getCurrent());
            pages.setSize(realTimeCalls.getSize());
            pages.setTotal(realTimeCalls.getTotal());
            pages.setPages(realTimeCalls.getPages());
            if (ObjectUtil.isNotEmpty(realTimeCalls.getRecords())) {
                List<Long> callIds = realTimeCalls.getRecords().stream().map(FunnelRealTimeCallListWrapper::getId).collect(Collectors.toList());
                //发送通知
                List<Map<String, Object>> sendDriverMaps = openFunnelMapper.selectSendDriverByCallIds(callIds, 0);
                Map<Long, List<Map<String, Object>>> sendDriverToMap = sendDriverMaps.stream().collect(Collectors.groupingBy(m -> MapUtil.getLong(m, "callId")));

                //接收通知的回调
                List<CallScreenLog> callBackScreenLogs = callScreenLogMapper.selectList(Wrappers.<CallScreenLog>lambdaQuery().in(CallScreenLog::getCallId, callIds));
                Map<Long, List<CallScreenLog>> screenToMap = callBackScreenLogs.stream().collect(Collectors.groupingBy(CallScreenLog::getCallId, Collectors.toList()));


                List<Map<String, Object>> grabMaps = driverGrabRealtimePersistedMapper.selectGrabDriverNumByCallIds(callIds);
                Map<Long, Integer> grabMap = grabMaps.stream().collect(Collectors.toMap(m -> MapUtil.getLong(m, "callId"), m -> MapUtil.getInt(m, "grabNum")));
                List<Ride> rides = rideService.getListByCallIds(callIds);
                Map<Long, Ride> rideMap = rides.stream().collect(Collectors.toMap(Ride::getRefId, Function.identity()));

                for (Object call : page.getRecords()) {
                    RealtimeCall realtimeCall = (RealtimeCall) call;
                    Map<String, Object> realtimeCallMap = BeanUtil.beanToMap(realtimeCall);
                    Ride r = rideMap.get(realtimeCall.getId());
                    if (ObjectUtil.isNotNull(r)) {
                        realtimeCallMap.put("state", r.getState().name());
                        realtimeCallMap.put("rideId", r.getId());
                        realtimeCallMap.put("receiveDate", ObjectUtil.isNotNull(r.getCreatedAt()) ? r.getCreatedAt() : "-");
                        RideCancellation rideCancellation = rideCancellationMapper.selectOne(Wrappers.<RideCancellation>lambdaQuery().eq(RideCancellation::getRideId, r.getId()));
                        if (ObjectUtil.isNotNull(rideCancellation)) {
                            realtimeCallMap.put("cancellationDate", ObjectUtil.isNotNull(rideCancellation.getCreatedAt()) ? rideCancellation.getCreatedAt() : "-");
                            if (rideCancellation.getTriggerBy() == RideCancellationTriggerBy.driver) {
                                realtimeCallMap.put("state", "cancel_driver");
                            } else if (rideCancellation.getTriggerBy() == RideCancellationTriggerBy.customer) {
                                realtimeCallMap.put("state", "cancel_customer");
                            } else if (rideCancellation.getTriggerBy() == RideCancellationTriggerBy.admin) {
                                realtimeCallMap.put("state", "cancel_admin");
                            }
                        }
                    } else if (realtimeCall.getState() == AppointmentCallState.cancelled) {
                        realtimeCallMap.put("cancellationDate", ObjectUtil.isNotNull(realtimeCall.getUpdatedAt()) ? realtimeCall.getUpdatedAt() : "-");
                        if (realtimeCall.getState() == AppointmentCallState.cancelled) {
                            realtimeCallMap.put("state", "cancel_customer");
                        } else if (realtimeCall.getState() == AppointmentCallState.covered) {
                            realtimeCallMap.put("state", "cancel_system");
                        }
                    }
                    //乘客信息
                    User user = userMapper.selectById(realtimeCall.getCustomerId());
                    if (ObjectUtil.isNotNull(user)) {
                        realtimeCallMap.put("userName", user.getName());
                    }
                    //发送司机数
                    List<Map<String, Object>> sendList = sendDriverToMap.get(realtimeCall.getId());
                    if (CollUtil.isEmpty(sendList)) {
                        realtimeCallMap.put("sendNum", 0);
                        realtimeCallMap.put("talkNum", 0);
                        realtimeCallMap.put("notTalkNum", 0);
                    } else {
                        List<Long> driverIds = sendList.stream().map(x -> MapUtil.getLong(x, "driverId")).distinct().collect(Collectors.toList());
                        List<CallScreenLog> sendMaps = screenToMap.get(realtimeCall.getId());
                        if (CollUtil.isNotEmpty(sendMaps)) {
                            realtimeCallMap.put("sendNum", driverIds.size());
                        } else {
                            realtimeCallMap.put("sendNum", 0);
                        }

                        int talkNum = 0, notTalkNum = 0;

                        if (ObjectUtil.isNull(sendMaps)) {
                            realtimeCallMap.put("talkNum", 0);
                            realtimeCallMap.put("notTalkNum", 0);
                        } else {

                            List<CallScreenLog> distinct = sendMaps.stream().collect(Collectors.collectingAndThen(Collectors.toMap(CallScreenLog::getDriverId, Function.identity(), (existing, replacement) -> existing.getScreened() == 1 ? existing : replacement), map -> new ArrayList<>(map.values())));
                            talkNum = distinct.stream().filter(m -> m.getScreened() == 1).collect(Collectors.toList()).size();
                            realtimeCallMap.put("talkNum", talkNum);
                            realtimeCallMap.put("notTalkNum", (driverIds.size() - talkNum) >= 0 ? (driverIds.size() - talkNum) : 0);
                        }
                        //抢单司机数
                        realtimeCallMap.put("grabCount", ObjectUtil.isNull(grabMap.get(realtimeCall.getId())) ? 0 : grabMap.get(realtimeCall.getId()));

                    }
                    list.add(realtimeCallMap);
                }

            }

        } else if ("phone".equals(type)) {
            pages = simpleCallMapper.selectPage(page, Wrappers.<SimpleCall>lambdaQuery().eq(SimpleCall::getCantonId, cantonId).in(SimpleCall::getCategory, Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment)).between(SimpleCall::getCreatedAt, stime, etime).orderByDesc(SimpleCall::getId));
            list = getSimpleCallPage(pages);

        } else if ("help_old".equals(type)) {
            pages = simpleCallMapper.selectPage(page, Wrappers.<SimpleCall>lambdaQuery().eq(SimpleCall::getCantonId, cantonId).in(SimpleCall::getCategory, Arrays.asList(SimpleCallRideCategory.old_simple)).between(SimpleCall::getCreatedAt, stime, etime).orderByDesc(SimpleCall::getId));
            list = getSimpleCallPage(pages);
        }
        res.setRecords(list);
        res.setCurrent(pages.getCurrent());
        res.setPages(pages.getPages());
        res.setSize(pages.getSize());
        res.setTotal(pages.getTotal());
        return RestResult.success(res);
    }

    @Override
    public RestResult rideJoinDriver(Map<String, Object> params) {
        String type = MapUtil.getStr(params, "type");
        String callId = MapUtil.getStr(params, "callId");
        Map<String, Object> result = new HashMap<>();
        if ("real_time".equals(type)) {
            RealtimeCall realtimeCall = realtimeCallMapper.selectById(callId);
            String passengerLocate = realtimeCall.getOrigin().getLongitude() + "," + realtimeCall.getOrigin().getLatitude();
            List<Map<String, Object>> drivers = driverMapper.getRideJoinDriver(callId);
            DriverGrabRealtimePersisted driverGrabRealtimePersisted = driverGrabRealtimePersistedMapper.selectOne(Wrappers.<DriverGrabRealtimePersisted>lambdaQuery().eq(DriverGrabRealtimePersisted::getRealtimeCallId, callId));
            List<RealtimeCallMqttMsgMongo> list = realtimeCallMqttMsgMongoRepository.findByRealtimeCallId(realtimeCall.getId());
            Map<Long, RealtimeCallMqttMsgMongo> resultMap = list.stream().sorted(Comparator.comparing(RealtimeCallMqttMsgMongo::getCreatedAt))// 按时间升序排序
                    .collect(Collectors.toMap(
                            RealtimeCallMqttMsgMongo::getDriverId,
                            record -> record,// 保留每个 driverId 的第一个出现的记录（最早时间）
                            (existing, replacement) -> existing // 如果存在重复的 driverId，保留第一个
                    ));
            QueryWrapper<ReportEventLog> wrapper = new QueryWrapper<>();
            wrapper.apply("extra LIKE CONCAT('%\"callId\":', {0}, '%')", callId)
                    .eq("event_type", "call")
                    .in("from_app", Arrays.asList("isu", "driver"));
            List<ReportEventLog> eventLogs = reportEventLogMapper.selectList(wrapper);
            Map<Long, ReportEventLog> resultReportMap = eventLogs.stream().sorted(Comparator.comparing(ReportEventLog::getCreatedAt))// 按时间升序排序
                    .collect(Collectors.toMap(
                            ReportEventLog::getAccountId,
                            record -> record,// 保留每个 driverId 的第一个出现的记录（最早时间）
                            (existing, replacement) -> existing // 如果存在重复的 driverId，保留第一个
                    ));
            RealtimeCallMqttMsgMongo firstElement = list.stream()
                    .sorted(Comparator.comparing(RealtimeCallMqttMsgMongo::getCreatedAt))
                    .findFirst()
                    .orElse(null);
            for (Map<String, Object> map : drivers) {
                Long driverId = MapUtil.getLong(map, "driverId");
                List<DriverConfirmCallPersisted> confirmCallPersisteds = driverConfirmCallPersistedMapper.selectList(Wrappers.<DriverConfirmCallPersisted>lambdaQuery().eq(DriverConfirmCallPersisted::getDriverId, driverId).eq(DriverConfirmCallPersisted::getCallId, callId));
                map.put("receiveDate", CollUtil.isNotEmpty(confirmCallPersisteds) ? confirmCallPersisteds.get(0).getCreatedAt() : "-");
                map.put("sendMqttDate", ObjectUtil.isNotNull(resultMap.get(driverId)) ? resultMap.get(driverId).getCreatedAtStr() : "-");
                map.put("receiveMqttDate", ObjectUtil.isNotNull(resultReportMap.get(driverId)) ? resultReportMap.get(driverId).getCreatedAt() : "-");


            }
            List<Map<String, Object>> driverTags = getDriverAndTag(callId, drivers, type);
            int talkNum = 0, notTalkNum = 0;
            if (ObjectUtil.isNotNull(driverTags)) {
                List<Map<String, Object>> hasBroadcast_true = driverTags.stream().filter(driverTag -> MapUtil.getBool(driverTag, "hasBroadcast")).collect(Collectors.toList());
                talkNum = hasBroadcast_true.size();
                List<Map<String, Object>> hasBroadcast_false = driverTags.stream().filter(driverTag -> !MapUtil.getBool(driverTag, "hasBroadcast")).collect(Collectors.toList());
                notTalkNum = hasBroadcast_false.size();
            }
            result.put("talkNum", talkNum);
            result.put("notTalkNum", notTalkNum);
            result.put("passengerLocate", passengerLocate);
            result.put("drivers", getDriverAndTag(callId, drivers, type));

            result.put("joinedriverIds", ObjectUtil.isNull(driverGrabRealtimePersisted) ? "" : driverGrabRealtimePersisted.getJoinedDriverIds());
            result.put("winnerDriverId", ObjectUtil.isNull(driverGrabRealtimePersisted) ? "" : driverGrabRealtimePersisted.getWinnerDriverId());
            result.put("sendCallDate", DateUtil.formatTime(realtimeCall.getCreatedAt()));
            result.put("createDate", DateUtil.formatTime(realtimeCall.getCreatedAt()));//叫单时间
//            if (realtimeCall.getNeedPrepay()) {
//                CallPrepayOrder callPrepayOrder = callPrepayOrderMapper.selectOne(Wrappers.<CallPrepayOrder>lambdaQuery()
//                        .eq(CallPrepayOrder::getCallId, realtimeCall.getId())
//                        .eq(CallPrepayOrder::getCallCategory, 0));
//                result.put("payTime", ObjectUtil.isNotNull(callPrepayOrder) ? DateUtil.formatTime(callPrepayOrder.getCreatedAt()) : "-");//付款时间
//            } else {
//                result.put("payTime", DateUtil.formatTime(realtimeCall.getCreatedAt()));
//            }
            result.put("payTime", ObjectUtil.isNull(firstElement) ? "-" : DateUtil.formatTime(firstElement.getCreatedAt()));

        } else if ("phone".equals(type) || "help_old".equals(type)) {
            SimpleCall simpleCall = simpleCallMapper.selectById(callId);
            String passengerLocate = simpleCall.getOrigin().getLongitude() + "," + simpleCall.getOrigin().getLatitude();
            List<Map<String, Object>> drivers = openFunnelMapper.getSimpleCallRideJoinDriver(callId);
            DriverGrabSimpleCallPersisted driverGrabSimpleCallPersisted = driverGrabSimpleCallPersistedMapper.selectOne(Wrappers.<DriverGrabSimpleCallPersisted>lambdaQuery().eq(DriverGrabSimpleCallPersisted::getSimpleCallId, callId).orderByDesc(DriverGrabSimpleCallPersisted::getCreatedAt).last("limit 1"));
            for (Map<String, Object> map : drivers) {
                Long driverId = MapUtil.getLong(map, "driverId");
                List<DriverConfirmCallPersisted> confirmCallPersisteds = driverConfirmCallPersistedMapper.selectList(Wrappers.<DriverConfirmCallPersisted>lambdaQuery().eq(DriverConfirmCallPersisted::getDriverId, driverId).eq(DriverConfirmCallPersisted::getCallId, callId));
                map.put("receiveDate", CollUtil.isNotEmpty(confirmCallPersisteds) ? confirmCallPersisteds.get(0).getCreatedAt() : "-");
            }
            if (ObjectUtil.isNotNull(simpleCall.getScheduleTime()) && simpleCall.getCallMode() == CallMode.all) {
                result.put("callType", "appointment");
            }
            result.put("passengerLocate", passengerLocate);
            result.put("drivers", getDriverAndTag(callId, drivers, type));
            result.put("joinedriverIds", ObjectUtil.isNull(driverGrabSimpleCallPersisted) ? "" : driverGrabSimpleCallPersisted.getJoinedDriverIds());
            result.put("winnerDriverId", ObjectUtil.isNull(driverGrabSimpleCallPersisted) ? "" : driverGrabSimpleCallPersisted.getWinnerDriverId());
            result.put("sendCallDate", simpleCall.getCreatedAt());
        }
        return RestResult.success(result);
    }

    private List<ReportEventLog> getReportLogsByCallId(Long callId) {
        DataSource dsTestDB = DSFactory.get("group_test");
        List<ReportEventLog> logs = new ArrayList<>();
        try {
            String sql = "select * from prod_report_event_log " + "where event_type = 'call' " + "AND from_app IN ('isu', 'driver') " + " AND JSON_UNQUOTE(JSON_EXTRACT(extra, '$.callId')) = ?";
            List<Entity> records = Db.use(dsTestDB).query(sql, callId);

            for (Entity record : records) {
                ReportEventLog reportEventLog = convertToReportEventLog(record);
                logs.add(reportEventLog);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return logs;
    }

    private ReportEventLog convertToReportEventLog(Entity record) {
        ReportEventLog log = new ReportEventLog();

        // 使用泛型方式获取字段
        log.setId(record.get("id") != null ? Long.parseLong(record.get("id").toString()) : null);
        log.setAccountId(record.get("account_id") != null ? Long.parseLong(record.get("account_id").toString()) : null);
        log.setEventType(record.getStr("event_type"));
        log.setFromApp(record.getStr("from_app"));
        Object createdAt = record.get("created_at");
        if (createdAt != null) {
            log.setCreatedAt(createdAt.toString());
        }


        return log;
    }

    private List<Map<String, Object>> getDriverAndTag(String callId, List<Map<String, Object>> drivers, String type) {
        List<Map<String, Object>> result = new ArrayList<>();
        List<CallScreenLog> callScreenLogs = callScreenLogMapper.selectList(Wrappers.<CallScreenLog>lambdaQuery().eq(CallScreenLog::getCallId, callId));
        Map<Long, CallScreenLog> collect = callScreenLogs.stream().collect(Collectors.toMap(CallScreenLog::getDriverId, Function.identity(), (existing, replacement) -> existing.getScreened() == 1 ? existing : replacement));
        List<RealtimeCallExcludeTaxiMongo> byCallIds = realtimeCallExcludeTaxiRepository.findByCallId(Long.parseLong(callId));
        Map<Long, RealtimeCallExcludeTaxiMongo> toMap = byCallIds.stream().collect(Collectors.toMap(
                RealtimeCallExcludeTaxiMongo::getTaxiId,
                Function.identity(),
                (existing, replacement) -> existing.getTimes() >= replacement.getTimes() ? existing : replacement
        ));

        List<DriverGrabCallInfoMongo> grabByCallId = driverGrabCallInfoMongoRepository.getByCallId(Long.parseLong(callId));
        Map<Long, DriverGrabCallInfoMongo> grabToMap = grabByCallId.stream().collect(Collectors.toMap(DriverGrabCallInfoMongo::getDriverId, Function.identity(),
                (first, next) -> next));
        for (Map<String, Object> driver : drivers) {
            Long driverId = MapUtil.getLong(driver, "driverId");
            Long taxiId = MapUtil.getLong(driver, "taxiId");
            RealtimeCallExcludeTaxiMongo realtimeCallExcludeTaxiMongo = toMap.get(taxiId);
            if (ObjectUtil.isNotNull(realtimeCallExcludeTaxiMongo)) {
                driver.put("tag", realtimeCallExcludeTaxiMongo.getReason());
            }
            DriverGrabCallInfoMongo driverGrabCallInfoMongo = grabToMap.get(driverId);
            if (ObjectUtil.isNotNull(driverGrabCallInfoMongo)) {
                driver.put("grabTime", DateUtil.formatDateTime(driverGrabCallInfoMongo.getGrabTime()));
            } else {
                driver.put("grabTime", "-");
            }
            CallScreenLog callScreenLog = collect.get(driverId);
            driver.put("reason", "");
            if (ObjectUtil.isNotNull(callScreenLog)) {
                driver.put("sendCallDate", DateUtil.formatTime(callScreenLog.getCreatedAt()));
                if (callScreenLog.getScreened() == 0) {
                    driver.put("hasBroadcast", false);
                    String reason = matchReasonTemplate(callScreenLog.getReason());
                    driver.put("reason", reason);
                } else {
                    String reason = matchReasonTemplate(callScreenLog.getReason());
                    driver.put("reason", "未知原因".equals(reason) ? "-" : reason);
                    driver.put("hasBroadcast", true);
                }
            } else {
                driver.put("hasBroadcast", false);
                driver.put("receiveDate", "");
                driver.put("reason", "-");
            }

            result.add(driver);
        }


        return result;
    }

    private String matchReasonTemplate(String data) {
        // 模板集合
        String[] templates = {"taxi last position (.*?) far away", "deviceId (.*?) not match", "distance (.*?) outside", "driverInfo empty", "timeout", "appoint timeout", "in ride", "new call but lost locate", "force show", "judgement passed", "below straight distance threshold", "ignore distance", "cancelled", "picked"};
        String result = "";
        for (int i = 0; i < templates.length; i++) {
            String template = templates[i];
            Pattern pattern = Pattern.compile(template);
            Matcher matcher = pattern.matcher(data);
            if (matcher.find()) {
                if (i == 0) {
                    result = "实时位置超出";
                } else if (i == 1) {
                    result = "设备id不匹配";
                } else if (i == 2) {
                    result = "超过听单距离";
                } else if (i == 3) {
                    result = "司机信息错误";
                } else if (i == 4) {
                    result = "超时";
                } else if (i == 5) {
                    result = "预约单超时";
                } else if (i == 6) {
                    result = "行程中";
                } else if (i == 7) {
                    result = "车机没有有效的定位";
                } else if (i == 8) {
                    result = "强制报单";
                } else if (i == 9) {
                    result = "条件满足";
                } else if (i == 10) {
                    result = "符合直线距离条件";
                } else if (i == 11) {
                    result = "无视距离";
                } else if (i == 12) {
                    result = "订单被取消";
                } else if (i == 13) {
                    result = "订单已被抢";
                } else {
                    result = "未知原因";
                }
                break;
            }
        }
        return result;

    }

    @Override
    public RestResult getCancelAnalyse(Map<String, Object> params) {
        Integer cantonId = MapUtil.getInt(params, "cantonId");
        String type = MapUtil.getStr(params, "type");
        Date todayStart = DateUtil.beginOfDay(DateUtil.date());
        Date todayEnd = DateUtil.endOfDay(DateUtil.date());
        Long callCancelCount = 0L;
        Long callSystemCancelCount = 0L;

        List<Map<String, Object>> listMap = null;
        List<Map<String, Object>> countMaps = null;
        List<Map<String, Object>> sysCancelMaps = null;
        if ("real_time".equals(type)) {
            listMap = openFunnelMapper.getRealTimeCancelAnalyse(todayStart, todayEnd, cantonId);
            List<CallTaxiKpi> callTaxiKpis = callTaxiKpiMapper.selectList(Wrappers.<CallTaxiKpi>lambdaQuery().eq(CallTaxiKpi::getCantonId, cantonId).eq(CallTaxiKpi::getYmd, DateUtil.formatDate(DateUtil.date())));
            callCancelCount = callTaxiKpis.stream().map(CallTaxiKpi::getCallCancel).reduce(0L, (a, b) -> a + b);
            callSystemCancelCount = callTaxiKpis.stream().map(CallTaxiKpi::getCallCover).reduce(0L, (a, b) -> a + b);
        } else if ("phone".equals(type)) {
            listMap = openFunnelMapper.getSimpleCallCancelAnalyse(Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment), todayStart, todayEnd, cantonId);
            countMaps = openFunnelMapper.getSimpleCallCancel(Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment), todayStart, todayEnd, cantonId);
        } else if ("help_old".equals(type)) {
            listMap = openFunnelMapper.getSimpleCallCancelAnalyse(Arrays.asList(SimpleCallRideCategory.old_simple), todayStart, todayEnd, cantonId);
            countMaps = openFunnelMapper.getSimpleCallCancel(Arrays.asList(SimpleCallRideCategory.old_simple), todayStart, todayEnd, cantonId);
        }
        Map<String, Object> result = new HashMap<>();
        long cancelWithin1Minute = listMap.stream().filter(ride -> DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) < 1).count();
        long cancelWithin2Minute = listMap.stream().filter(ride -> DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) >= 1 && DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) < 2).count();
        long cancelWithin3Minute = listMap.stream().filter(ride -> DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) >= 2 && DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) < 3).count();
        long cancelWithin4Minute = listMap.stream().filter(ride -> DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) >= 3 && DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) < 4).count();
        long cancelWithin5Minute = listMap.stream().filter(ride -> DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) >= 4 && DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) < 5).count();
        long cancelWithin5LaterMinute = listMap.stream().filter(ride -> DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) >= 5).count();


        result.put("cancelWithin1Minute", CollUtil.isNotEmpty(listMap) ? cancelWithin1Minute : 0);
        result.put("cancelWithin2Minute", CollUtil.isNotEmpty(listMap) ? cancelWithin2Minute : 0);
        result.put("cancelWithin3Minute", CollUtil.isNotEmpty(listMap) ? cancelWithin3Minute : 0);
        result.put("cancelWithin4Minute", CollUtil.isNotEmpty(listMap) ? cancelWithin4Minute : 0);
        result.put("cancelWithin5Minute", CollUtil.isNotEmpty(listMap) ? cancelWithin5Minute : 0);
        result.put("cancelWithin5LaterMinute", CollUtil.isNotEmpty(listMap) ? cancelWithin5LaterMinute : 0);
        result.put("cancelCount", CollUtil.isNotEmpty(listMap) ? listMap.size() : 0);
        result.put("callCancelCount", callCancelCount);
        result.put("sysCancelCount", callSystemCancelCount);

        return RestResult.success(result);
    }

    @Override
    public RestResult getCallCancelAnalyse(Map<String, Object> params) {
        Integer cantonId = MapUtil.getInt(params, "cantonId");
        String type = MapUtil.getStr(params, "type");
        Date todayStart = DateUtil.beginOfDay(DateUtil.date());
        Date todayEnd = DateUtil.endOfDay(DateUtil.date());

        List<Map<String, Object>> listMap = null;
        if ("real_time".equals(type)) {
            listMap = openFunnelMapper.getRealTimeCancelCall(todayStart, todayEnd, cantonId, 2);
        } else if ("phone".equals(type)) {
            listMap = openFunnelMapper.getSimpleCallCancel(Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment), todayStart, todayEnd, cantonId);
        } else if ("help_old".equals(type)) {
            listMap = openFunnelMapper.getSimpleCallCancel(Arrays.asList(SimpleCallRideCategory.old_simple), todayStart, todayEnd, cantonId);
        }
        Map<String, Object> result = new HashMap<>();
        long cancelWithin1Minute = listMap.stream().filter(ride -> DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) < 1).count();
        long cancelWithin2Minute = listMap.stream().filter(ride -> DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) >= 1 && DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) < 2).count();
        long cancelWithin3Minute = listMap.stream().filter(ride -> DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) >= 2 && DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) < 3).count();
        long cancelWithin4Minute = listMap.stream().filter(ride -> DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) >= 3 && DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) < 4).count();
        long cancelWithin5Minute = listMap.stream().filter(ride -> DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) >= 4 && DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) < 5).count();
        long cancelWithin5LaterMinute = listMap.stream().filter(ride -> DateUtil.between(MapUtil.getDate(ride, "cancelTime"), MapUtil.getDate(ride, "rideTime"), DateUnit.MINUTE) >= 5).count();


        result.put("cancelWithin1Minute", CollUtil.isNotEmpty(listMap) ? cancelWithin1Minute : 0);
        result.put("cancelWithin2Minute", CollUtil.isNotEmpty(listMap) ? cancelWithin2Minute : 0);
        result.put("cancelWithin3Minute", CollUtil.isNotEmpty(listMap) ? cancelWithin3Minute : 0);
        result.put("cancelWithin4Minute", CollUtil.isNotEmpty(listMap) ? cancelWithin4Minute : 0);
        result.put("cancelWithin5Minute", CollUtil.isNotEmpty(listMap) ? cancelWithin5Minute : 0);
        result.put("cancelWithin5LaterMinute", CollUtil.isNotEmpty(listMap) ? cancelWithin5LaterMinute : 0);
        result.put("cancelCount", CollUtil.isNotEmpty(listMap) ? listMap.size() : 0);

        return RestResult.success(result);
    }

    @Override
    public RestResult get30DayRepeatCallNum(Map<String, Object> params) {
        Integer cantonId = MapUtil.getInt(params, "cantonId");
        String type = MapUtil.getStr(params, "type");

        Date endDate = DateUtil.date();
        Date startDate = DateUtil.offsetDay(endDate, -30);
        List<Map<String, Object>> result = null;
        if ("real_time".equals(type)) {
            result = callTaxiKpiMapper.selectRealTimeRepeatCallNum(cantonId, DateUtil.format(startDate, DatePattern.PURE_DATE_PATTERN), DateUtil.format(endDate, DatePattern.PURE_DATE_PATTERN));
        } else if ("phone".equals(type)) {
            result = openFunnelMapper.selectSimpleRepeatCallNum(Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment), startDate, endDate, cantonId);
        } else if ("help_old".equals(type)) {
            result = openFunnelMapper.selectSimpleRepeatCallNum(Arrays.asList(SimpleCallRideCategory.old_simple), startDate, endDate, cantonId);
        }
        return RestResult.success(result);

    }

    @Override
    public RestResult getCancelList(Map<String, Object> params) {
        Integer cantonId = MapUtil.getInt(params, "cantonId");
        String type = MapUtil.getStr(params, "type");

        Date startDate = DateUtil.beginOfDay(DateUtil.date());
        Date endDate = DateUtil.endOfDay(DateUtil.date());
        if ("real_time".equals(type)) {
            List<CancelDataVO> resultMap = openFunnelMapper.getCancelList(startDate, endDate, cantonId);


            List<CancelDataVO> adminCancel = resultMap.stream().filter(e -> e.getTriggerBy() == 3).collect(Collectors.toList());
            List<CancelDataVO> userCancel = resultMap.stream().filter(e -> e.getTriggerBy() == 1).collect(Collectors.toList());
            List<CancelDataVO> driverCancel = resultMap.stream().filter(e -> e.getTriggerBy() == 2).collect(Collectors.toList());
            List<CancelDataVO> systemCancel = resultMap.stream().filter(e -> e.getTriggerBy() == 4).collect(Collectors.toList());

            Map<String, Object> map = new HashMap<>();
            map.put("cancelData", resultMap);
            map.put("adminCancel", CollUtil.isNotEmpty(adminCancel) ? adminCancel.size() : 0);
            map.put("userCancel", CollUtil.isNotEmpty(userCancel) ? userCancel.size() : 0);
            map.put("driverCancel", CollUtil.isNotEmpty(driverCancel) ? driverCancel.size() : 0);
            map.put("systemCancel", CollUtil.isNotEmpty(systemCancel) ? systemCancel.size() : 0);

            return RestResult.success(map);
        }


        return null;
    }

    @Override
    public RestResult receiveAndFinishByHour(Map<String, Object> params) {
        Integer cantonId = MapUtil.getInt(params, "cantonId");
        String type = MapUtil.getStr(params, "type");
        Date todayStart = DateUtil.beginOfDay(DateUtil.date());
        Date todayEnd = DateUtil.endOfDay(DateUtil.date());
        List<Map<String, Object>> pickRates = null;
        List<Map<String, Object>> finishRates = null;
        if ("real_time".equals(type)) {
            pickRates = callTaxiKpiMapper.getPickRate(DateUtil.format(todayStart, DatePattern.PURE_DATE_PATTERN), cantonId);
            finishRates = callTaxiKpiMapper.getFinishRates(DateUtil.format(todayStart, DatePattern.PURE_DATE_PATTERN), cantonId);

        } else if ("phone".equals(type)) {
            pickRates = new ArrayList<>();
            finishRates = new ArrayList<>();
        } else if ("help_old".equals(type)) {
            pickRates = new ArrayList<>();
            finishRates = new ArrayList<>();
        }
        Map<String, Object> result = new HashMap<>();
        result.put("pickRates", pickRates);
        result.put("finishRates", finishRates);
        return RestResult.success(result);

    }


    private List<Map<String, Object>> getSimpleCallPage(Page<Object> pages) {
        List<Map<String, Object>> list = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(pages.getRecords())) {
            List<Long> callIds = pages.getRecords().stream().map(item -> ((SimpleCall) item).getId()).collect(Collectors.toList());
            List<Map<String, Object>> sendMaps = driverConfirmCallPersistedMapper.selectSendNumByCallIds(callIds, 2);
            Map<Long, Integer> sendMap = sendMaps.stream().collect(Collectors.toMap(m -> MapUtil.getLong(m, "callId"), m -> MapUtil.getInt(m, "driverNum")));
            List<Map<String, Object>> grabMaps = driverGrabSimpleCallPersistedMapper.selectGrabDriverNumByCallIds(callIds);
            Map<Long, Integer> grabMap = grabMaps.stream().collect(Collectors.toMap(m -> MapUtil.getLong(m, "callId"), m -> MapUtil.getInt(m, "grabNum")));
            List<SimpleCallRide> rides = simpleCallRideMapper.selectList(Wrappers.<SimpleCallRide>lambdaQuery().in(SimpleCallRide::getRefId, callIds));
            Map<Long, SimpleCallRide> rideMap = rides.stream().collect(Collectors.toMap(SimpleCallRide::getRefId, Function.identity()));
            for (Object call : pages.getRecords()) {
                SimpleCall simpleCall = (SimpleCall) call;
                Map<String, Object> simpleCallMap = BeanUtil.beanToMap(simpleCall);
                SimpleCallRide r = rideMap.get(simpleCall.getId());
                if (ObjectUtil.isNotNull(r)) {
                    simpleCallMap.put("state", r.getState().name());
                    simpleCallMap.put("rideId", r.getId());
                    simpleCallMap.put("receiveDate", ObjectUtil.isNotNull(r.getCreatedAt()) ? r.getCreatedAt() : "-");
                    SimpleCallRideCancellation rideCancellation = simpleCallRideCancellationMapper.selectOne(Wrappers.<SimpleCallRideCancellation>lambdaQuery().eq(SimpleCallRideCancellation::getRideId, r.getId()));
                    if (ObjectUtil.isNotNull(rideCancellation)) {
                        simpleCallMap.put("cancellationDate", ObjectUtil.isNotNull(rideCancellation.getCreatedAt()) ? rideCancellation.getCreatedAt() : "-");
                        if (rideCancellation.getTriggerBy() == SimpleCallRideCancellationTriggerBy.driver) {
                            simpleCallMap.put("state", "cancel_driver");
                        } else if (rideCancellation.getTriggerBy() == SimpleCallRideCancellationTriggerBy.customer) {
                            simpleCallMap.put("state", "cancel_customer");
                        } else if (rideCancellation.getTriggerBy() == SimpleCallRideCancellationTriggerBy.admin) {
                            simpleCallMap.put("state", "cancel_admin");
                        }
                    }
                } else if (simpleCall.getState() == SimpleCallState.cancelled || simpleCall.getState() == SimpleCallState.cancel_admin || simpleCall.getState() == SimpleCallState.covered) {
                    simpleCallMap.put("state", simpleCall.getState().name());
                    simpleCallMap.put("cancellationDate", ObjectUtil.isNotNull(simpleCall.getUpdatedAt()) ? simpleCall.getUpdatedAt() : "-");
                } else {
                    simpleCallMap.put("state", simpleCall.getState().name());
                }
                //乘客信息
                SimpleCallCustomer user = simpleCallCustomerMapper.selectById(simpleCall.getCustomerId());
                if (ObjectUtil.isNotNull(user)) {
                    simpleCallMap.put("userName", user.getName());
                }
                //发送司机数
                simpleCallMap.put("sendNum", ObjectUtil.isNull(sendMap.get(simpleCall.getId())) ? 0 : sendMap.get(simpleCall.getId()));
                //抢单司机数
                simpleCallMap.put("grabCount", ObjectUtil.isNull(grabMap.get(simpleCall.getId())) ? 0 : grabMap.get(simpleCall.getId()));
                list.add(simpleCallMap);
            }
        }
        return list;
    }


    /**
     * 数据字段：司机/乘客ID 、  姓名、车牌、手机号、订单号、里程、下单时间、订单状态、司机奖励金额/乘客奖励金额、乘客首次注册时间、首次完单时间
     *
     * @param current
     * @param size
     * @param cantonId
     * @param type
     * @return
     */
    @Override
    public RestResult getScanRide(Integer current, Integer size, Integer cantonId, String type, String startDate, String endDate) {
        Dict resultDict = new Dict();
        Page page = new Page(current, size);
        if (StrUtil.isBlank(startDate) || StrUtil.isBlank(endDate)) {
            startDate = DateUtil.format(DateUtil.date(), DatePattern.NORM_DATE_PATTERN);
            endDate = DateUtil.format(DateUtil.date(), DatePattern.NORM_DATE_PATTERN);
        }
        startDate = startDate + " 00:00:00";
        endDate = endDate + " 23:59:59";
        Page<Map<String, Object>> pages = openFunnelMapper.getScanRide(page, startDate, endDate);
        List<Map<String, Object>> resultList = new ArrayList<>();
        if (CollUtil.isNotEmpty(pages.getRecords())) {
            for (Map<String, Object> map : pages.getRecords()) {
                Map<String, Object> result = new HashMap<>();
                Integer rewardCategory = MapUtil.getInt(map, "rewardCategory");
                Integer grantedId = MapUtil.getInt(map, "grantedId");
                result.put("grantedId", grantedId);
                result.put("rewardCategory", rewardCategory);

                Long callId = MapUtil.getLong(map, "callId");
                result.put("callId", callId);
                result.put("driverRewardValue", MapUtil.getStr(map, "driverRewardValue"));
                result.put("userRewardValue", MapUtil.getStr(map, "userRewardValue"));
                RealtimeCall realtimeCall = realtimeCallMapper.selectById(callId);
                if (ObjectUtil.isNotNull(realtimeCall)) {
                    User user = userMapper.selectById(realtimeCall.getCustomerId());
                    result.put("userName", user.getName());
                    result.put("userPhone", user.getMobileNumber());
                }
                if (realtimeCall.getState() == AppointmentCallState.pick) {
                    Ride ride = rideMapper.selectOne(Wrappers.<Ride>lambdaQuery().eq(Ride::getRefId, callId));
                    result.put("rideId", ride.getId());
                    result.put("state", ride.getState().name());
                    if (ObjectUtil.isNotNull(ride)) {
                        Driver driver = driverMapper.selectById(ride.getDriverId());
                        result.put("driverName", driver.getName());
                        result.put("driverPhone", driver.getMobileNumber());

                        Taxi taxi = taxiMapper.selectById(driver.getTaxiId());
                        result.put("plateNumber", taxi.getPlateNumber());

                        RideOrder rideOrder = rideOrderMapper.selectOne(Wrappers.<RideOrder>lambdaQuery().eq(RideOrder::getRideId, ride.getId()));

                        result.put("payTime", ObjectUtil.isNotNull(rideOrder) ? DateUtil.formatDateTime(rideOrder.getLastPayTime()) : "-");
                        result.put("amount", ObjectUtil.isNotNull(rideOrder) ? rideOrder.getAmount() : "-");
                    }
                } else {
                    result.put("state", realtimeCall.getState().name());
                }
                if (ObjectUtil.isNotNull(realtimeCall)) {
                    result.put("createDate", DateUtil.formatDateTime(realtimeCall.getCreatedAt()));
                }
                resultList.add(result);
            }
            pages.setRecords(resultList);
            resultDict.put("pages", pages);

            //乘客奖励
            //司机奖励
            Page<Map<String, Object>> pageCount = openFunnelMapper.getScanRide(new Page(1, 10000), startDate, endDate);
            BigDecimal userRewardValue = pageCount.getRecords().stream().map(map -> MapUtil.get(map, "userRewardValue", BigDecimal.class)).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal driverRewardValue = pageCount.getRecords().stream().map(map -> MapUtil.get(map, "driverRewardValue", BigDecimal.class)).reduce(BigDecimal.ZERO, BigDecimal::add);
            resultDict.put("userRewardValue", userRewardValue);
            resultDict.put("driverRewardValue", driverRewardValue);
        } else {
            resultDict.put("userRewardValue", 0);
            resultDict.put("driverRewardValue", 0);

        }


        return RestResult.success(resultDict);
    }

    @Override
    public RestResult saveActivityReward(Map<String, Object> params) {
        Long rideId = MapUtil.getLong(params, "rideId");
        String userRewardValue = MapUtil.getStr(params, "userRewardValue");
        String driverRewardValue = MapUtil.getStr(params, "driverRewardValue");

        Ride ride = rideMapper.selectOne(Wrappers.<Ride>lambdaQuery().eq(Ride::getId, rideId));
        if (ObjectUtil.isNull(ride)) {
            return RestResult.failed("操作失败,行程不存在");
        }
        if (StrUtil.isNotBlank(userRewardValue)) {
//            RealtimeCall realtimeCall = realtimeCallMapper.selectById(ride.getRefId());
//            if (ObjectUtil.isNotNull(realtimeCall)) {
//                if (realtimeCall.getNeedPrepay()) {//预付单
//
//
//                }
//            }


            openFunnelMapper.saveActivityReward(ride.getRefId(), ride.getUserId(), 0, userRewardValue, "31", "31");
        }
        if (StrUtil.isNotBlank(driverRewardValue)) {
            openFunnelMapper.saveActivityReward(ride.getRefId(), ride.getDriverId(), 1, driverRewardValue, "31", "31");
        }
        return RestResult.success();
    }

    @SneakyThrows
    @Override
    public void exportActivityReward(String startDate, String endDate, HttpServletResponse response) {
        Dict resultDict = new Dict();
        Page page = new Page(1, 100000);
        if (StrUtil.isBlank(startDate) || StrUtil.isBlank(endDate)) {
            startDate = DateUtil.format(DateUtil.date(), DatePattern.NORM_DATE_PATTERN);
            endDate = DateUtil.format(DateUtil.date(), DatePattern.NORM_DATE_PATTERN);
        }
        startDate = startDate + " 00:00:00";
        endDate = endDate + " 23:59:59";
        Page<Map<String, Object>> pages = openFunnelMapper.getScanRide(page, startDate, endDate);
        List<Map<String, Object>> resultList = new ArrayList<>();
        if (CollUtil.isNotEmpty(pages.getRecords())) {
            for (Map<String, Object> map : pages.getRecords()) {
                Map<String, Object> result = new HashMap<>();
                Integer rewardCategory = MapUtil.getInt(map, "rewardCategory");
                Integer grantedId = MapUtil.getInt(map, "grantedId");
                result.put("grantedId", grantedId);
                result.put("rewardCategory", rewardCategory);

                Long callId = MapUtil.getLong(map, "callId");
                result.put("callId", callId);
                result.put("driverRewardValue", MapUtil.getStr(map, "driverRewardValue"));
                result.put("userRewardValue", MapUtil.getStr(map, "userRewardValue"));
                RealtimeCall realtimeCall = realtimeCallMapper.selectById(callId);
                if (ObjectUtil.isNotNull(realtimeCall)) {
                    User user = userMapper.selectById(realtimeCall.getCustomerId());
                    result.put("userName", user.getName());
                    result.put("userPhone", user.getMobileNumber());
                }
                if (realtimeCall.getState() == AppointmentCallState.pick) {
                    Ride ride = rideMapper.selectOne(Wrappers.<Ride>lambdaQuery().eq(Ride::getRefId, callId));
                    result.put("rideId", ride.getId());
                    result.put("state", ride.getState().name());
                    if (ObjectUtil.isNotNull(ride)) {
                        Driver driver = driverMapper.selectById(ride.getDriverId());
                        result.put("driverName", driver.getName());
                        result.put("driverPhone", driver.getMobileNumber());

                        Taxi taxi = taxiMapper.selectById(driver.getTaxiId());
                        result.put("plateNumber", taxi.getPlateNumber());

                        RideOrder rideOrder = rideOrderMapper.selectOne(Wrappers.<RideOrder>lambdaQuery().eq(RideOrder::getRideId, ride.getId()));

                        result.put("payTime", ObjectUtil.isNotNull(rideOrder) ? DateUtil.formatDateTime(rideOrder.getLastPayTime()) : "-");
                        result.put("amount", ObjectUtil.isNotNull(rideOrder) ? rideOrder.getAmount() : "-");
                    }
                } else {
                    result.put("state", realtimeCall.getState().name());
                }
                if (ObjectUtil.isNotNull(realtimeCall)) {
                    result.put("createDate", DateUtil.formatDateTime(realtimeCall.getCreatedAt()));
                }
                resultList.add(result);
            }
            pages.setRecords(resultList);
            resultDict.put("pages", pages);

            //乘客奖励
            //司机奖励
            Page<Map<String, Object>> pageCount = openFunnelMapper.getScanRide(new Page(1, 10000), startDate, endDate);
            BigDecimal userRewardValue = pageCount.getRecords().stream().map(map -> MapUtil.get(map, "userRewardValue", BigDecimal.class)).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal driverRewardValue = pageCount.getRecords().stream().map(map -> MapUtil.get(map, "driverRewardValue", BigDecimal.class)).reduce(BigDecimal.ZERO, BigDecimal::add);
            resultDict.put("userRewardValue", userRewardValue);
            resultDict.put("driverRewardValue", driverRewardValue);
        } else {
            resultDict.put("userRewardValue", 0);
            resultDict.put("driverRewardValue", 0);

        }

        List<Map<String, Object>> rows = new ArrayList<>();
        for (Map<String, Object> map : pages.getRecords()) {
            Map<String, Object> row = new LinkedHashMap<>();
            row.put("呼叫ID", MapUtil.getStr(map, "callId"));
            row.put("行程ID", MapUtil.getStr(map, "rideId"));
            row.put("乘客名称", MapUtil.getStr(map, "userName"));
            row.put("乘客手机", MapUtil.getStr(map, "userPhone"));
            row.put("车牌号", MapUtil.getStr(map, "plateNumber"));
            row.put("司机名称", MapUtil.getStr(map, "driverName"));
            row.put("司机手机", MapUtil.getStr(map, "driverPhone"));
            row.put("下单时间", MapUtil.getStr(map, "createDate"));
            row.put("付款时间", MapUtil.getStr(map, "payTime"));
            row.put("司机奖励", MapUtil.getStr(map, "driverRewardValue"));
            row.put("乘客奖励", MapUtil.getStr(map, "userRewardValue"));
            rows.add(row);
        }
        ExcelWriter writer = ExcelUtil.getWriter();
        StyleSet style = writer.getStyleSet();
        CellStyle cellStyle = style.getHeadCellStyle();
        cellStyle.setWrapText(true);
        writer.write(rows, true);
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=reward_list.xls");
        ServletOutputStream out = response.getOutputStream();

        writer.flush(out, true);
        writer.close();
        IoUtil.close(out);
    }

    @SneakyThrows
    @Override
    public RestResult exportTodayRide(Integer cantonId, String type, String date, HttpServletResponse response) {
        IPage<Map<String, Object>> res = new Page<>();
        List<Map<String, Object>> list = new ArrayList<>();
        Page page = new Page(1, 10000);
        Page<Object> pages = null;

        DateTime startDate = null;
        DateTime endDate = null;
        if (StrUtil.isNotBlank(date)) {
            startDate = DateUtil.parse(date + " 00:00:00");
            endDate = DateUtil.parse(date + " 23:59:59");
        } else {
            startDate = DateUtil.beginOfDay(DateUtil.date());
            endDate = DateUtil.endOfDay(DateUtil.date());
        }
        if ("real_time".equals(type)) {
            IPage<FunnelRealTimeCallListWrapper> realTimeCalls = openFunnelMapper.getRealTimeCalls(page, cantonId, startDate, endDate);
            pages = new Page<>();
            pages.setCurrent(realTimeCalls.getCurrent());
            pages.setSize(realTimeCalls.getSize());
            pages.setTotal(realTimeCalls.getTotal());
            pages.setPages(realTimeCalls.getPages());
            if (ObjectUtil.isNotEmpty(realTimeCalls.getRecords())) {
                List<Long> callIds = realTimeCalls.getRecords().stream().map(FunnelRealTimeCallListWrapper::getId).collect(Collectors.toList());
                //发送通知
                List<Map<String, Object>> sendDriverMaps = openFunnelMapper.selectSendDriverByCallIds(callIds, 0);
                Map<Long, List<Map<String, Object>>> sendDriverToMap = sendDriverMaps.stream().collect(Collectors.groupingBy(m -> MapUtil.getLong(m, "callId")));

                //接收通知的回调
                List<CallScreenLog> callBackScreenLogs = callScreenLogMapper.selectList(Wrappers.<CallScreenLog>lambdaQuery().in(CallScreenLog::getCallId, callIds));
                Map<Long, List<CallScreenLog>> screenToMap = callBackScreenLogs.stream().collect(Collectors.groupingBy(CallScreenLog::getCallId, Collectors.toList()));


                List<Map<String, Object>> grabMaps = driverGrabRealtimePersistedMapper.selectGrabDriverNumByCallIds(callIds);
                Map<Long, Integer> grabMap = grabMaps.stream().collect(Collectors.toMap(m -> MapUtil.getLong(m, "callId"), m -> MapUtil.getInt(m, "grabNum")));
                List<Ride> rides = rideService.getListByCallIds(callIds);
                Map<Long, Ride> rideMap = rides.stream().collect(Collectors.toMap(Ride::getRefId, Function.identity()));

                for (Object call : page.getRecords()) {
                    RealtimeCall realtimeCall = (RealtimeCall) call;
                    Map<String, Object> realtimeCallMap = BeanUtil.beanToMap(realtimeCall);
                    Ride r = rideMap.get(realtimeCall.getId());
                    if (ObjectUtil.isNotNull(r)) {
                        realtimeCallMap.put("state", r.getState().name());
                        realtimeCallMap.put("rideId", r.getId());
                        realtimeCallMap.put("receiveDate", ObjectUtil.isNotNull(r.getCreatedAt()) ? r.getCreatedAt() : "-");
                        RideCancellation rideCancellation = rideCancellationMapper.selectOne(Wrappers.<RideCancellation>lambdaQuery().eq(RideCancellation::getRideId, r.getId()));
                        if (ObjectUtil.isNotNull(rideCancellation)) {
                            realtimeCallMap.put("cancellationDate", ObjectUtil.isNotNull(rideCancellation.getCreatedAt()) ? rideCancellation.getCreatedAt() : "-");
                            if (rideCancellation.getTriggerBy() == RideCancellationTriggerBy.driver) {
                                realtimeCallMap.put("state", "cancel_driver");
                            } else if (rideCancellation.getTriggerBy() == RideCancellationTriggerBy.customer) {
                                realtimeCallMap.put("state", "cancel_customer");
                            } else if (rideCancellation.getTriggerBy() == RideCancellationTriggerBy.admin) {
                                realtimeCallMap.put("state", "cancel_admin");
                            }
                        }
                    } else if (realtimeCall.getState() == AppointmentCallState.cancelled) {
                        realtimeCallMap.put("cancellationDate", ObjectUtil.isNotNull(realtimeCall.getUpdatedAt()) ? realtimeCall.getUpdatedAt() : "-");
                        if (realtimeCall.getState() == AppointmentCallState.cancelled) {
                            realtimeCallMap.put("state", "cancel_customer");
                        } else if (realtimeCall.getState() == AppointmentCallState.covered) {
                            realtimeCallMap.put("state", "cancel_system");
                        }
                    }
                    //乘客信息
                    User user = userMapper.selectById(realtimeCall.getCustomerId());
                    if (ObjectUtil.isNotNull(user)) {
                        realtimeCallMap.put("userName", user.getName());
                        realtimeCallMap.put("userMobileNumber", user.getMobileNumber());
                    }
                    //发送司机数
                    List<Map<String, Object>> sendList = sendDriverToMap.get(realtimeCall.getId());
                    if (CollUtil.isEmpty(sendList)) {
                        realtimeCallMap.put("sendNum", 0);
                        realtimeCallMap.put("talkNum", 0);
                        realtimeCallMap.put("notTalkNum", 0);
                    } else {
                        List<Long> driverIds = sendList.stream().map(x -> MapUtil.getLong(x, "driverId")).distinct().collect(Collectors.toList());
                        List<CallScreenLog> sendMaps = screenToMap.get(realtimeCall.getId());
                        if (CollUtil.isNotEmpty(sendMaps)) {
                            realtimeCallMap.put("sendNum", driverIds.size());
                        } else {
                            realtimeCallMap.put("sendNum", 0);
                        }

                        int talkNum = 0, notTalkNum = 0;

                        if (ObjectUtil.isNull(sendMaps)) {
                            realtimeCallMap.put("talkNum", 0);
                            realtimeCallMap.put("notTalkNum", 0);
                        } else {

                            List<CallScreenLog> distinct = sendMaps.stream().collect(Collectors.collectingAndThen(Collectors.toMap(CallScreenLog::getDriverId, Function.identity(), (existing, replacement) -> existing.getScreened() == 1 ? existing : replacement), map -> new ArrayList<>(map.values())));
                            talkNum = distinct.stream().filter(m -> m.getScreened() == 1).collect(Collectors.toList()).size();
                            realtimeCallMap.put("talkNum", talkNum);
                            realtimeCallMap.put("notTalkNum", (driverIds.size() - talkNum) >= 0 ? (driverIds.size() - talkNum) : 0);
                        }
                        //抢单司机数
                        realtimeCallMap.put("grabCount", ObjectUtil.isNull(grabMap.get(realtimeCall.getId())) ? 0 : grabMap.get(realtimeCall.getId()));

                    }
                    list.add(realtimeCallMap);
                }

            }

        } else if ("phone".equals(type)) {
            pages = simpleCallMapper.selectPage(page, Wrappers.<SimpleCall>lambdaQuery().eq(SimpleCall::getCantonId, cantonId).in(SimpleCall::getCategory, Arrays.asList(SimpleCallRideCategory.phone, SimpleCallRideCategory.phone_appointment)).between(SimpleCall::getCreatedAt, DateUtil.beginOfDay(DateUtil.date()), DateUtil.endOfDay(DateUtil.date())).orderByDesc(SimpleCall::getId));
            list = getSimpleCallPage(pages);

        } else if ("help_old".equals(type)) {
            pages = simpleCallMapper.selectPage(page, Wrappers.<SimpleCall>lambdaQuery().eq(SimpleCall::getCantonId, cantonId).in(SimpleCall::getCategory, Arrays.asList(SimpleCallRideCategory.old_simple)).between(SimpleCall::getCreatedAt, DateUtil.beginOfDay(DateUtil.date()), DateUtil.endOfDay(DateUtil.date())).orderByDesc(SimpleCall::getId));
            list = getSimpleCallPage(pages);
        }

        List<Map<String, Object>> rows = new ArrayList<>();
        for (Map<String, Object> map : list) {
            Map<String, Object> row = new LinkedHashMap<>();
            row.put("呼叫ID", MapUtil.getStr(map, "id"));
            Integer prepayState = MapUtil.getInt(map, "prepayState");
            Boolean needPrepay = MapUtil.getBool(map, "needPrepay");
            if (needPrepay) {
                String text = "预付单";
                if (ObjectUtil.isNotNull(prepayState) && prepayState == 2) {
                    row.put("预付信息", text + "(已支付)");
                } else {
                    row.put("预付信息", text + "(未支付)");
                }
            } else {
                row.put("预付信息", "非预付单");
            }
            row.put("行程ID", MapUtil.getStr(map, "rideId"));
            row.put("乘客名称", MapUtil.getStr(map, "userName"));
            row.put("乘客手机号", MapUtil.getStr(map, "userMobileNumber"));
            row.put("上车点", MapUtil.getStr(map, "originName"));
            row.put("下车点", MapUtil.getStr(map, "destinationName"));
            row.put("发送司机数", MapUtil.getStr(map, "sendNum"));
            row.put("已报/未报", MapUtil.getInt(map, "talkNum") + "/" + MapUtil.getInt(map, "notTalkNum"));
            row.put("抢单司机数", MapUtil.getStr(map, "grabCount"));
            row.put("状态", formatState(MapUtil.getStr(map, "state")));
            row.put("订单创建时间", DateUtil.date(MapUtil.getLong(map, "createdAt")).toString());
            Long receiveDate = MapUtil.getLong(map, "receiveDate");
            if (ObjectUtil.isNotNull(receiveDate)) {
                row.put("接单时间", DateUtil.date(receiveDate).toString());
            }
            Long cancellationDate = MapUtil.getLong(map, "cancellationDate");
            if (ObjectUtil.isNotNull(cancellationDate)) {
                row.put("取消时间", DateUtil.date(cancellationDate).toString());
            }
            rows.add(row);
        }

//        ExcelWriter writer = ExcelUtil.getWriter();
//        StyleSet style = writer.getStyleSet();
//        CellStyle cellStyle = style.getHeadCellStyle();
//        cellStyle.setWrapText(true);
//        writer.write(rows, true);
//        response.setContentType("application/vnd.ms-excel;charset=utf-8");
//        response.setHeader("Content-Disposition", "attachment;filename=reward_list.xls");
//        ServletOutputStream out = response.getOutputStream();
//
//        writer.flush(out, true);
//        writer.close();
//        IoUtil.close(out);

        return RestResult.success(rows);

    }

    private String formatState(String state) {
        switch (state) {
            case "waiting":
            case "nopick":
                return "等待接单";
            case "contracted":
            case "pick":
                return "司机已接单";
            case "inArea":
                return "司机到达上车点";
            case "pickuping":
            case "pickUp":
                return "司机接到乘客";
            case "started":
            case "start":
                return "行程开始";
            case "ended":
            case "end":
                return "行程结束";
            case "completed":
                return "订单完成";
            case "marked":
                return "已评价";
            case "cancel_admin":
                return "管理员取消";
            case "cancel_driver":
                return "司机取消";
            case "cancel_customer":
            case "cancelled":
                return "乘客取消";
            case "cancel_system":
            case "covered":
                return "系统取消";
            default:
                return "未知状态";
        }
    }

}
